package org.rednebula.framework.core;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.rednebula.framework.core.exception.LandException;
import org.rednebula.framework.core.log.Log;
import org.rednebula.framework.core.utils.InternalConstant;


public class BasicEnvironment extends AbstractEnvironmentImpl implements
		Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 5442657223574132314L;

	protected transient ClassLoader classLoader;

	protected static Log log = Log.getLog(BasicEnvironment.class);

	protected Map<String, IContext> contexts = new LinkedHashMap<String, IContext>();
	    
    protected Map<String,String> servicesClassify=new HashMap<String,String>();
    
	public BasicEnvironment() {
		super();
		this.initClassify();
	}
    
	protected void initClassify(){
		//servicesClassify.put(InternalConstant.PROCEDUREENGINE, IContext.PROCEDUREENGINE);
		servicesClassify.put(InternalConstant.JBPM_PROCESSENGINE_NAME, IContext.FLOW);
		servicesClassify.put(InternalConstant.REPOSITORYSERVICE, IContext.FLOW);
		servicesClassify.put(InternalConstant.HISTORYSERVICE, IContext.FLOW);
		servicesClassify.put(InternalConstant.IDENTITYSERVICE, IContext.FLOW);
		servicesClassify.put(InternalConstant.TASKSERVCIE, IContext.FLOW);
		servicesClassify.put(InternalConstant.MANAGEMENTSERVICE, IContext.FLOW);
		servicesClassify.put(InternalConstant.EXECUTIONSERVICE, IContext.FLOW);
		
		servicesClassify.put(InternalConstant.HTTPSESSIONCONTEXT, IContext.SESSION);
		
		servicesClassify.put(InternalConstant.VALUESTACKCONTEXT, IContext.VALUESTACKCONTEXT);
		
		servicesClassify.put(InternalConstant.INTERNALSERVLETCONTEXT, IContext.APPLICTION);
		
		servicesClassify.put(InternalConstant.HTTPREQUESTCONTEXT, IContext.REQUEST);
		
		servicesClassify.put(InternalConstant.SPRINGAPPLICATIONCONTEXT, IContext.SPRING);
	}
	
	protected Map<String, IContext> getContexts(){
		return this.contexts;
	}
	
	protected Map<String,String> getServicesClassify(){
		return this.servicesClassify;
	}
	
	@Override
	public void close() {
		popEnvironment();
		Iterator<IContext> iter=getContexts().values().iterator();
		while(iter.hasNext())
			iter.next().close();
		getContexts().clear();
		getServicesClassify().clear();
		engine=null;
	}


	@Override
	public <T> T lookup(String key) {
		return (T) get(key, this.getDefaultSearchOrder(), true);
	}

	public <T> T lookup(Class<T> type) {
		return get(type, (String[]) null);
	}

	public <T> T get(Class<T> type, String[] searchOrder) {
		
		if(IProcedureEngine.class.isAssignableFrom(type)){
			return (T)getEngine();
		}
		if (searchOrder == null) {
			searchOrder = getDefaultSearchOrder();
		}
		for (String contextName : searchOrder) {
			IContext context = getContexts().get(contextName);
			T object = context.get(type);

			if (object != null)
				return object;
		}
		return null;
	}

	public Object get(String name, String[] searchOrder) {
		return get(name, searchOrder, true);
	}

	public Object get(String name, String[] searchOrder, boolean nullAllowed)
			throws LandException {
		 if(getServicesClassify().containsKey(name)){
			 return getContext(getServicesClassify().get(name)).get(name);
		 }
		if (searchOrder == null) {
			searchOrder = getDefaultSearchOrder();
		}
		for (String contextName : searchOrder) {
			IContext context = getContexts().get(contextName);
			if (context.has(name)) {
				return context.get(name);
			}
		}
		if (nullAllowed) {
			return null;
		} else {
			throw new LandException("Null value found for " + name
					+ " but null is not allowed");
		}
	}

	public Object get(String name, boolean nullAllowed) throws LandException {
		return get(name, this.getDefaultSearchOrder(), nullAllowed);
	}

	@Override
	public ClassLoader getClassLoader() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public IContext getContext(String contextName) {
		return getContexts().get(contextName);
	}

	@Override
	public IContext removeContext(IContext context) {
		return getContexts().remove(context);
	}

	@Override
	public IContext removeContext(String contextName) {
		return getContexts().remove(contextName);
	}

	@Override
	public void setContext(IContext context) {
		getContexts().put(context.getName(), context);
	}

	protected String[] getDefaultSearchOrder() {
		int size = getContexts().size();
		String[] defaultSearchOrder = new String[size];

		int index = size;
		for (String contextName : getContexts().keySet()) {
			defaultSearchOrder[--index] = contextName;
		}

		return defaultSearchOrder;
	}
}
